package edu.study.common.web;

import com.querydsl.core.types.dsl.BooleanExpression;
import edu.study.api.common.dto.BaseDto;
import edu.study.api.common.dto.BaseReqDto;
import edu.study.api.common.dto.BaseRespDto;
import edu.study.api.common.dto.PageReqDto;
import edu.study.api.common.dto.PageRespDto;
import edu.study.api.common.enums.BaseResultCodeEnum;
import edu.study.common.model.BaseDO;
import edu.study.common.util.BeanCopy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 公共Web请求和响应处理类
 */
@Slf4j
//TODO mcj 为什么不叫CommonWebUtil了
public class MongoDBUtil {

    public static BaseRespDto addBaseDO(BaseReqDto req, BaseDO baseDO, MongoRepository dao) {
        BaseRespDto resp = new BaseRespDto();
        try {
            BeanCopy.copyFromSrcToDest(req, baseDO);
            log.info("##>MongoDBUtil.addBaseDO, baseDO={}", baseDO);
            dao.insert(baseDO);
        } catch (Exception e){
            log.error("<<<CommonWebUtil.addBaseDO end, e={}", e);
            if(e instanceof DuplicateKeyException){
                String message = e.getMessage();
                int begin = message.indexOf("{");
                int end = message.indexOf("}");
                message = message.substring(begin, end+1);
                resp.setResultFail(BaseResultCodeEnum.DUPLICATE_KEY_EXCEPTION.getMessage()+message);
            } else {
                resp.setResultFail(BaseResultCodeEnum.OPERATE_DB_ERROR);
            }
        }
        return resp;
    }

    public static <T> PageRespDto<T> queryBaseDOList(PageReqDto req, BooleanExpression query, QuerydslPredicateExecutor dao, Class<? extends BaseDto> clazz) {
        PageRespDto<T> resp = new PageRespDto<>();
        Pageable pageable = toPageable(req);
        log.info("##>CommonWebUtil.queryBaseDOList, query={}, pageable={}", query, pageable);
        Page<BaseDO> pageList = dao.findAll(query, pageable);
        if(!CollectionUtils.isEmpty(pageList.getContent())){
            List<T> list = new ArrayList<>();
            for (BaseDO baseDO:pageList.getContent()) {
                BaseDto baseDto = null;
                try {
                    baseDto = clazz.newInstance();//反射无参构造器的
                } catch (Exception e) {
                    log.error("<<<CommonWebUtil.queryBaseDOList end, e={}", e);
                    resp.setResultFail(BaseResultCodeEnum.EXCEPTION);
                    return resp;
                }
                BeanCopy.copyFromSrcToDest(baseDO, baseDto);
                list.add((T) baseDto);
            }
            resp.setData(list);
            resp.setPages(pageList.getTotalPages());
            resp.setTotal(pageList.getTotalElements());
            resp.setPageNum(req.getPageNum());
        }
        return resp;
    }

    public static List<Sort.Order> toOrders(String... sortBys){
        if(sortBys == null || sortBys.length <1){
            return Collections.emptyList();
        }
        return toOrders(Arrays.asList(sortBys));
    }

    public static List<Sort.Order> toOrders(List<String> sortBys){
        if(sortBys == null || sortBys.isEmpty()){
            return Collections.emptyList();
        }
        List<Sort.Order> orders = new ArrayList<>();
        for(String sortBy : sortBys){
            if(sortBy == null || sortBy.isEmpty() || sortBy.trim().isEmpty()) {continue;}
            String[] propDirection = sortBy.split("-");
            String prop = propDirection[0];
            if(propDirection.length>1){
                String direction = propDirection[1];
                if("asc".equals(direction)){
                    orders.add(Sort.Order.asc(prop));
                }else if("desc".equals(direction)){
                    orders.add(Sort.Order.desc(prop));
                }
            }
        }
        return orders;
    }

    public static Pageable toPageable(int page , int size , String... sortBys){
        return PageRequest.of(page,size, ObjectUtils.isEmpty(sortBys)? Sort.unsorted() : Sort.by(toOrders(sortBys)));
    }

    public static Pageable toPageable(PageReqDto req){
        int pageNum = req.getPageNum();
        if(pageNum < 0){
            pageNum = 0;
        }
        int pageSize = req.getPageSize();
        if(pageSize < 1){
            pageSize = 1;
        }
        return PageRequest.of(pageNum, pageSize, ObjectUtils.isEmpty(req.getOrderBy())? Sort.unsorted() : Sort.by(toOrders(req.getOrderBy())));
    }

    public static <T> PageRespDto<T> toPageResp(Page<T> pageData){
        PageRespDto<T> respDto = new PageRespDto<>();
        respDto.setData(pageData.getContent());
        respDto.setPages(pageData.getTotalPages());
        respDto.setTotal(pageData.getTotalElements());
        return respDto;
    }
}
